import CryptoJS from 'crypto-js'

/**
 * 内容加密相关安全类
 * @author lixiao
 */
export default class Security {
  static SECRET = process.env.SECURITY_SECRET;

  /**
   * 获取加密解密使用的向量
   * @param {*} data
   */
  static getCryptoIV (pos) {
    let str = this.SECRET + this.SECRET
    console.log('getCryptoIV:', str)
    return CryptoJS.enc.Utf8.parse(str.slice(pos, pos + 16))
  }

  /**
   * 获取加密解密使用的密钥
   * @param {*} data
   */
  static getCryptoKey () {
    return CryptoJS.enc.Utf8.parse(this.SECRET)
  }

  /**
   * 生成随机数字位置
   * @param {*} data
   */
  static randomPosition (lower, upper) {
    // return Math.floor(Math.random() * 32)
    return Math.floor(Math.random() * (upper - lower) + lower)
  }

  /**
   * 生成随机字符串
   * @param {*} data
   */
  static randomString (length) {
    let text = ''
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    for (let i = 0; i < length; i++) {
      text += possible.charAt(Math.floor(Math.random() * possible.length))
    }
    return text
  }

  static getTimestamp () {
    const date = new Date()
    const timeString = Date.parse(date).toString()
    return timeString.slice(0, timeString.length - 3)
  }

  /**
   * 生成随机字符串
   * @param {*} data
   */
  static base64Encode (raw) {
    let data = CryptoJS.enc.Utf8.parse(raw)
    return CryptoJS.enc.Base64.stringify(data)
  }

  /**
   * 生成随机字符串
   * @param {*} data
   */
  static base64Decode (raw) {
    let data = CryptoJS.enc.Base64.parse(raw)
    return data.toString(CryptoJS.enc.Utf8)
  }

  /**
   * 生成随机字符串
   * @param {*} data
   */
  static parseUtf8 (data) {
    return CryptoJS.enc.Utf8.parse(data)
  }

  /* AES加密
   * param : message 密文
   * return : encrypted string base64字符串
   */
  static encryptAES (data, key, iv) {
    return CryptoJS.AES.encrypt(this.parseUtf8(data), key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    }).toString()
  }

  /* AES解密
   * param : message 密文 !!!本身就是base64!!!!只有Base64类型的字符串密文才能对其进行解密
   * return : decrypted string 明文
   */
  static decryptAES (message, key, iv) {
    let decrypted = CryptoJS.AES.decrypt(message, key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    })
    return decrypted.toString(CryptoJS.enc.Utf8)
  }

  /**
   * 获取AES加密之后的字符串
   * @param {*} data 需要加密的字符串
   * @param {*} key 秘钥
   * @param {*} iv 偏移向量
   */
  static encrypt (data) {
    let pos = this.randomPosition(0, this.SECRET.length - 1)
    let prefix = String.fromCharCode(pos + 48)
    let key = this.getCryptoKey()
    let iv = this.getCryptoIV(pos)
    let encryptedData = prefix + this.encryptAES(data, key, iv)
    return this.base64Encode(encryptedData)
  }

  /**
   * 获取解密之后的字符串
   * @param {*} encrypted 需要解密的字符串
   * @param {*} key 秘钥
   * @param {*} iv 偏移向量
   */
  static decrypt (data) {
    let content = this.base64Decode(data)
    let posChar = content.substr(0, 1)
    let pos = posChar.charCodeAt() - 48
    let encryptData = content.substr(1)

    let key = this.getCryptoKey()
    let iv = this.getCryptoIV(pos)
    return this.decryptAES(encryptData, key, iv)
  }

  /**
   * 获取解密之后的字符串
   * @param {*} encrypted 需要解密的字符串
   * @param {*} key 秘钥
   * @param {*} iv 偏移向量
   */
  static generateSignature (data) {
    const nonce = this.randomString(32)
    let appid = process.env.APP_ID
    let secret = process.env.APP_SECRET
    let build = process.env.VERSION

    let params = Object.assign({
      appid: appid,
      timestamp: this.getTimestamp(),
      build: build
    }, data)

    let str = ''
    for (let key of Object.keys(params).sort()) {
      str += params[key]
    }
    let sign = CryptoJS.SHA256(secret + str + nonce).toString(CryptoJS.enc.Hex)

    return Object.assign(params, {
      nonce: nonce,
      sign: sign
    })
  }
}
